package com.zf.yichat.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


/**
 * ClassName: RSAUtil
 *
 * @Description: 签名工具类
 *
 */
@Slf4j
public class RSAUtil {
	

    private static final String KEY_ALGORTHM = "RSA";

    private static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    private static final String PUBLIC_KEY = "RSAPublicKey";

    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /** 密钥长度，用来初始化 */
    private static final int KEY_SIZE = 2048;
    static BouncyCastleProvider bcp = new BouncyCastleProvider();

    /**
     * 生成密钥
     * 
     * @return
     * @throws Exception
     */
    public static Map<String, Object> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator
                .getInstance(KEY_ALGORTHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);

        return keyMap;
    }

    /**
     * 取得公钥，并转化为String类型
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return CodeUtil.encryptBASE64(key.getEncoded());
    }

    /**
     * 取得私钥，并转化为String类型
     * 
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return CodeUtil.encryptBASE64(key.getEncoded());
    }

    /**
     * 生成密钥对文件
     * 
     * @return
     * @throws Exception
     */
    public static void genKeyPairFile(String path) throws Exception {
        Map<String, Object> keyPair = genKeyPair();

        String publicKey = getPublicKey(keyPair);
        String privateKey = getPrivateKey(keyPair);

        FileWriter pufos1 = null;
        FileWriter pufos2 = null;
        try {
            pufos1 = new FileWriter(path + "rsa_public.key");
            pufos1.write(publicKey);
            pufos2 = new FileWriter(path + "rsa_private.key");
            pufos2.write(privateKey);
        } catch (IOException e) {
            log.error("RSAUtil.genKeyPairFile error", e);
        } finally {
            if (pufos1 != null) {
                try {
                    pufos1.close();
                } catch (IOException e) {
                    log.error("RSAUtil.genKeyPairFile error", e);
                }
            }
            if (pufos2 != null) {
                try {
                    pufos2.close();
                } catch (IOException e) {
                    log.error("RSAUtil.genKeyPairFile error", e);
                }
            }
        }
    }

    /**
     * 用私钥加密
     * 
     * @param data
     *            加密数据
     * @param keyPath
     *            密钥
     * @return
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeySpecException 
     * @throws NoSuchPaddingException 
     * @throws InvalidKeyException 
     * @throws BadPaddingException 
     * @throws IllegalBlockSizeException 
     * @throws Exception
     */
    public static String encryptByPrivateKey(String data, String keyPath)
            throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        String privateKeyStr = getKeyStr(keyPath);
        /** 解密密钥 */
        byte[] keyBytes = CodeUtil.decryptBASE64(privateKeyStr);

        /** 取私钥 */
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
                keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        /** 对数据加密 */
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm(), bcp);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);

        return CodeUtil.encryptBASE64(cipher.doFinal(data.getBytes()));
    }

    /**
     * 用私钥解密
     * 
     * @param keyPath
     *            密钥
     * @return
     * @throws Exception
     */
    public static String decryptByPrivateKey(String data, String keyPath)
            throws Exception {
        String privateKeyStr = getKeyStr(keyPath);
        /** 对私钥解密 */
        byte[] keyBytes = CodeUtil.decryptBASE64(privateKeyStr);

        /** 取私钥 */
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
                keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        /** 对数据解密 */
        Cipher cipher = Cipher.getInstance(KEY_ALGORTHM, bcp);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte[] decryptedData = cipher.doFinal(CodeUtil.decryptBASE64(data));

        return new String(decryptedData);
    }

    /**
     * 用公钥加密
     * 
     * @param data
     *            加密数据
     * @param keyPath
     *            密钥
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data, String keyPath)
            throws Exception {
        String publicKeyStr = getKeyStr(keyPath);
        /** 对公钥解密 */
        byte[] keyBytes = CodeUtil.decryptBASE64(publicKeyStr);

        /** 取公钥 */
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        /** 对数据解密 */
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm(), bcp);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        byte[] encryptedData = cipher.doFinal(data.getBytes());
        return CodeUtil.encryptBASE64(encryptedData);
    }

    /**
     * 用公钥解密
     * 
     * @param data
     *            加密数据
     * @param keyPath
     *            密钥
     * @return
     * @throws Exception
     */
    public static String decryptByPublicKey(String data, String keyPath)
            throws Exception {
        String publicKeyStr = getKeyStr(keyPath);
        /** 对公钥解密 */
        byte[] keyBytes = CodeUtil.decryptBASE64(publicKeyStr);

        /** 取公钥 */
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        /** 对数据解密 */
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm(), bcp);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);

        byte[] decryptedData = cipher.doFinal(CodeUtil.decryptBASE64(data));

        return new String(decryptedData);
    }

    /**
     * 用私钥对信息生成数字签名
     * 
     * @param data
     *            加密数据
     * @param keyPath
     *            私钥
     * @return
     * @throws Exception
     */
    public static String sign(String data, String keyPath) {
        try {
            String privateKeyStr = getKeyStr(keyPath);
            /** 解密私钥 */
            byte[] keyBytes = CodeUtil.decryptBASE64(privateKeyStr);

            /** 构造PKCS8EncodedKeySpec对象 */
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
                    keyBytes);
 
            /** 指定加密算法 */
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);

            /** 取私钥匙对象 */
            PrivateKey privateKey = keyFactory
                    .generatePrivate(pkcs8EncodedKeySpec);

            /** 用私钥对信息生成数字签名 */
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(privateKey);
            signature.update(data.getBytes("ISO-8859-1"));

            return CodeUtil.encryptBASE64(signature.sign());
        } catch (Exception e) {
            log.error("RSAUtil.sign error", e);
            return "";
        }
    }

    /**
     * 校验数字签名
     * 
     * @param data
     *            加密数据
     * @param sign
     *            数字签名
     * @param keyPath
     *            公钥
     * @return
     * @throws Exception
     */
    public static boolean verify(String data, String sign, String keyPath) {
        try {
            String publicKeyStr = getKeyStr(keyPath);
            /** 解密公钥 */
            byte[] keyBytes = CodeUtil.decryptBASE64(publicKeyStr);

            /** 构造X509EncodedKeySpec对象 */
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
                    keyBytes);

            /** 指定加密算法 */
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);

            /** 取公钥匙对象 */
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(publicKey);
            signature.update(data.getBytes("ISO-8859-1"));

            /** 验证签名是否正常 */
            return signature.verify(CodeUtil.decryptBASE64(sign));
        } catch (Exception e) {
            log.error("RSAUtil.verify error", e);
            return false;
        }
    }

    /**
     * 校验数字签名
     * 
     * @param data
     *            加密数据
     * @param sign
     *            数字签名
     * @param keyPath
     *            公钥
     * @return
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String keyPath) {
        RSAPublicKey publicKey = null;
        try {
            String publicKeyStr = getKeyStr(keyPath);
            /** 解密公钥 */
            byte[] keyBytes = CodeUtil.decryptBASE64(publicKeyStr);

            /** 构造X509EncodedKeySpec对象 */
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
                    keyBytes);

            /** 指定加密算法 */
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);

            /** 取公钥匙对象 */
            publicKey = (RSAPublicKey) keyFactory
                    .generatePublic(x509EncodedKeySpec);
        } catch (Exception e) {
            log.error("RSAUtil.getPublicKey error", e);
        }

        return publicKey;
    }

    /**
     * 校验数字签名
     * 
     * @param data
     *            加密数据
     * @param sign
     *            数字签名
     * @param keyPath
     *            公钥
     * @return
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String keyPath) {
        RSAPrivateKey privateKey = null;
        try {
            String privateKeyStr = getKeyStr(keyPath);
            /** 解密私钥 */
            byte[] keyBytes = CodeUtil.decryptBASE64(privateKeyStr);

            /** 构造PKCS8EncodedKeySpec对象 */
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
                    keyBytes);

            /** 指定加密算法 */
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);

             /** 取私钥匙对象 */
            privateKey = (RSAPrivateKey) keyFactory
                    .generatePrivate(pkcs8EncodedKeySpec);
        } catch (Exception e) {
            log.error("RSAUtil.getPrivateKey error", e);
        }

        return privateKey;
    }

    /**
     * 根据密钥路径获取密钥
     * 
     * @param path
     *            密钥路径
     * @return 密钥
     */
    private static String getKeyStr(String path) {
        File file = new File(path);
        if (!file.exists() || !file.isFile()
        		|| CatalogAttackSafeUtil.validateUrl(path)) {
            return "";
        }

        String keyStr = "";
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            byte[] data = new byte[1024];
            StringBuffer s = new StringBuffer();
            int size;
            while ((size = fis.read(data)) != -1) {
                s.append(new String(data, 0, size));
            }
            keyStr = s.toString();
        } catch (Exception e) {
            log.error("RSAUtil.getKeyStr error", e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                log.error("RSAUtil.getKeyStr error", e);
            }
        }
        return keyStr;
    }

}
